package com.fhsk.file.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.crypto.SecureUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fhsk.file.business.entity.bo.user.UserBo;
import com.fhsk.file.business.entity.bo.user.UserConditionBo;
import com.fhsk.file.business.entity.dto.user.UserDto;
import com.fhsk.file.business.entity.vo.user.UserVo;
import com.fhsk.file.business.mapper.UserMapper;
import com.fhsk.file.business.service.IUserService;
import com.fhsk.file.business.common.JwtUtil;
import com.fhsk.file.common.enums.RedisKeyEnum;
import com.fhsk.file.common.enums.RoleTypeEnum;
import com.fhsk.file.common.enums.SexEnum;
import com.fhsk.file.common.enums.StatusEnum;
import com.fhsk.file.common.exception.AuthenticationException;
import com.fhsk.file.common.exception.UnifiedException;
import com.fhsk.file.common.utils.RSAUtil;
import com.fhsk.file.common.utils.RedisComponent;
import com.fhsk.file.common.vo.AjaxResult;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 用户信息 服务实现类
 * </p>
 *
 * @author AlienWare
 * @since 2022-06-02
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDto> implements IUserService {

    @Resource
    private UserMapper userMapper;

    @Autowired
    HttpServletRequest request;

    @Resource
    private RedisComponent redisComponent;

    @Value("${server.encrypt.privateKey}")
    private String privateKey;

    @Value("${initPassword}")
    private String initPassword;

    @Value("${loginSingle}")
    private boolean loginSingle;

    @Value("${shiro.jwt.expireTime}")
    private long expireTime;

    @Value("${passwordReg}")
    private String passwordReg;

    @Value("${passwordRegAlert}")
    private String passwordRegAlert;

    @Override
    public Map<String, Object> list(UserConditionBo conditionBo) {
        Map<String, Object> map = new HashMap<>(4);
        if (0 == conditionBo.getLimit()) {
            //limit为0，则不分页（查询全部）
            List<UserVo> list = baseMapper.getList(conditionBo);
            map.put("list", list);
            map.put("count", list.size());
        } else {
            //分页查询
            PageHelper.startPage(conditionBo.getPage(), conditionBo.getLimit(), conditionBo.getOrderBy());
            List<UserVo> list = baseMapper.getList(conditionBo);
            PageInfo<UserVo> pageInfo = new PageInfo<>(list);
            map.put("list", pageInfo.getList());
            map.put("count", pageInfo.getTotal());
        }
        return map;
    }

    @Override
    public AjaxResult addUser(UserBo userBo) {
        if (StringUtils.isBlank(userBo.getAccount())) {
            return AjaxResult.fail("账号不能为空");
        }

        if (null == userBo.getRoleType()) {
            return AjaxResult.fail("角色类型不能为空，请输入数字：1（超级管理员），2（普通管理员）");
        }

        if (null == RoleTypeEnum.getRoleTypeByCode(userBo.getRoleType())) {
            return AjaxResult.fail("角色类型不正确，请输入数字：1（超级管理员），2（普通管理员）");
        }

        if (StringUtils.isBlank(userBo.getRealName())) {
            return AjaxResult.fail("用户姓名不能为空");
        }

        if (null != userBo.getSex()) {
            if (null == SexEnum.getSexByCode(userBo.getSex())) {
                return AjaxResult.fail("性别不正确，请输入数字：1（男），2（女）");
            }
        } else {
            // 性别默认：男
            userBo.setSex(SexEnum.MALE.getCode());
        }

        UserDto userDto = baseMapper.selectOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getAccount, userBo.getAccount())
                .eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));
        if (null != userDto) {
            return AjaxResult.fail("该账号已被使用");
        }

        UserDto user = new UserDto();
        Date now = new Date();
        BeanUtils.copyProperties(userBo, user);
        user.setOid(IdUtil.simpleUUID());
        user.setPassword(SecureUtil.sha1(user.getAccount() + initPassword));
        user.setIsDelete(StatusEnum.NOT_DELETE.getCode());
        user.setIsLocked(StatusEnum.ENABLE.getCode());
        user.setRegisterTime(now);
        user.setCreateBy(getCurrentUser().getOid());
        user.setCreateTime(now);
        if (save(user)) {
            return AjaxResult.success("保存成功");
        } else {
            return AjaxResult.fail("保存失败");
        }
    }

    @Override
    public UserVo getDetail(UserBo user) {
        LambdaQueryWrapper<UserDto> lqw = new LambdaQueryWrapper<>();

        if (StringUtils.isNotBlank(user.getOid())) {
            lqw.eq(UserDto::getOid, user.getOid());
        }

        if (StringUtils.isNotBlank(user.getAccount())) {
            lqw.eq(UserDto::getAccount, user.getAccount());
        }

        if (null != user.getId()) {
            lqw.eq(UserDto::getId, user.getId());
        }

        UserDto userDto = getOne(lqw);
        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(userDto, userVo);
        userVo.setPassword(null);
        return userVo;
    }

    @Override
    public AjaxResult updateUser(UserBo userBo) {
        UserDto userDto = baseMapper.selectOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getOid, userBo.getOid()));
        if (null == userDto) {
            return AjaxResult.success("用户不存在");
        }

        UserDto user = new UserDto();
        BeanUtil.copyProperties(userBo, user);
        user.setId(userDto.getId());
        user.setUpdateBy(getCurrentUser().getOid());
        user.setUpdateTime(new Date());
        if (updateById(user)) {
            return AjaxResult.success("保存成功");
        } else {
            return AjaxResult.fail("保存失败");
        }
    }

    @Override
    public UserVo login(UserBo userBo) {
        log.debug("account:" + userBo.getAccount() + "------->>>  password" + userBo.getPassword());

        //获取当前用户信息
        UserDto userDto = getOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getAccount, userBo.getAccount())
                .eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));

        //判断当前用户是否存在
        if (userDto == null) {
            throw new UnifiedException(500, "用户名或者密码错误,请检查是否正确!");
        }

        //验证当前用户是否已经禁用
        if (StatusEnum.DISABLE.getCode().equals(userDto.getIsLocked())) {
            throw new UnifiedException(205, "当前用户已经被禁用,请联系管理员");
        }
        String password = "";
        try {
            String passwordStr = userBo.getPassword();
            password = RSAUtil.privateDecrypt(passwordStr, privateKey);
        } catch (Exception e) {
            log.error("密码解密失败!原因:{}", e.getMessage());
        }
        //给密码进行加密
        String encryPwd = generatePsd(userBo.getAccount(), password);
        //检验密码是否正确
        if (!encryPwd.equals(userDto.getPassword())) {
            throw new UnifiedException(500, "用户名或者密码错误,请检查是否正确!");
        }
        //进行token签发
        String token = JwtUtil.sign(userBo.getAccount(), encryPwd);

        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(userDto, userVo);
        userVo.setPassword(null);
        //检查是否是使用初始密码登录
        userVo.setInitPwd(checkIsInit(userBo.getAccount(), password));
        //存入签发的token
        userVo.setToken(token);

        request.setAttribute("currentUser", userVo);
        //每次登录完成后刷新缓存信息
        String userKey = RedisKeyEnum.USER.getValue() + userBo.getAccount();
        redisComponent.del(userKey);
        redisComponent.set(userKey, userDto);
        //登录完之后将token存入redisSet中
        String tokenKey = RedisKeyEnum.TOKEN.getValue() + userBo.getAccount();
        if (loginSingle) {
            //如果登录互顶,则每次登录都进行删除之前所有的token
            redisComponent.del(tokenKey);
        }
        redisComponent.sSet(tokenKey, token);
        //设置过期时间
        redisComponent.expire(tokenKey, (expireTime / 1000));

        // 保存上次登录时间
        UserDto user = new UserDto();
        user.setId(userDto.getId());
        user.setLastLoginTime(new Date());
        updateById(user);

        return userVo;
    }

    @Override
    public Boolean checkIsInit(String userAccount, String password) {
        UserDto userDto = getOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getAccount, userAccount)
                .eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));
        String encryPassword = userDto.getPassword();
        String initEncryPassword = generatePsd(userDto.getAccount(), initPassword);
        return initEncryPassword.equals(encryPassword);
    }

    @Override
    public UserVo getCurrentUser() {
        UserVo user = null;
        try {
            user = (UserVo) request.getAttribute("currentUser");
        } catch (RuntimeException e) {
            throw new AuthenticationException(406, "您的登录已失效，请退出重新登录！");
        }
        //如果没有获取到当前用户直接弹出登录失效!
        if (user == null) {
            throw new AuthenticationException(406, "您的登录已失效，请退出重新登录！");
        }

        return user;
    }

    @Override
    public Boolean changePassword(String account, String oldPassword, String password) {
        //首先先检验新密码是否通过正则
        String reg = StringUtils.isEmpty(passwordReg) ? "^(?=.*[0-9])(?=.*[a-z])(?=.*[!@#$%^&*,\\\\.])[0-9a-zA-Z!@#$%^&*,\\\\.]{8,12}$" : passwordReg;
        boolean match = ReUtil.isMatch(reg, password);
        if (!match) {
            throw new UnifiedException(HttpStatus.BAD_REQUEST.value(), StringUtils.isEmpty(passwordReg) ? "密码格式不正确,密码长度为8-12位，包含数字、大小写字母及特殊字符" : passwordRegAlert);
        }
        //其次判断旧密码是否与新密码一致
        String oldPsd = generatePsd(account, oldPassword);
        String newPsd = generatePsd(account, password);
        if (newPsd.equals(oldPsd)) {
            throw new UnifiedException(HttpStatus.BAD_REQUEST.value(), "新密码与旧密码不可以一致");
        }
        //最终校验原密码是否与库中密码相同
        UserDto orgin = baseMapper.selectOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getAccount, account)
                .eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));
        if (null == orgin) {
            throw new UnifiedException(HttpStatus.BAD_REQUEST.value(), "用户不存在");
        }

        if (!oldPsd.equals(orgin.getPassword())) {
            throw new UnifiedException(HttpStatus.BAD_REQUEST.value(), "原密码输入不正确,请重新输入");
        }

        UserDto user = new UserDto();
        user.setId(orgin.getId());
        user.setPassword(newPsd);
        user.setUpdateBy(getCurrentUser().getOid());
        user.setUpdateTime(new Date());
        boolean update = updateById(user);
        return update;
    }

    @Override
    public AjaxResult resetPassword(String userOid) {
        UserDto userDto = getOne(new LambdaQueryWrapper<UserDto>()
                .eq(UserDto::getOid, userOid)
                .eq(UserDto::getIsDelete, StatusEnum.NOT_DELETE.getCode()));

        //判断当前用户是否存在
        if (userDto == null) {
            throw new UnifiedException(500, "用户不存在");
        }

        //验证当前用户是否已经禁用
        if (StatusEnum.DISABLE.getCode().equals(userDto.getIsLocked())) {
            throw new UnifiedException(205, "当前用户已经被禁用,请联系管理员");
        }

        String psd = generatePsd(userDto.getAccount(), initPassword);
        UserDto user = new UserDto();
        user.setId(userDto.getId());
        user.setPassword(psd);
        user.setUpdateBy(getCurrentUser().getOid());
        user.setUpdateTime(new Date());
        updateById(user);

        return AjaxResult.success(initPassword);
    }

    @Override
    public void initPassword() {
        QueryWrapper<UserDto> wrapper = new QueryWrapper<>();
        wrapper.isNull("password");
        List<UserDto> userList = userMapper.selectList(wrapper);
        for (UserDto user : userList) {
            String account = user.getAccount();
            String password = SecureUtil.sha1(account + initPassword);
            user.setPassword(password);
            userMapper.updateById(user);
        }
    }

    /**
     * 生成密码
     *
     * @param salt
     * @param initPassword
     * @return
     */
    private String generatePsd(String salt, String initPassword) {
        return SecureUtil.sha1(salt + initPassword.trim());
    }

}
